#!/bin/awk -f

# Usage:
#     awk -f DTCC_VerifyManifest_and_WorkingDir.awk
#     or
#     nawk -f DTCC_VerifyManifest_and_WorkingDir.awk

#  Creator:    $Creator: Paul_M_Thompson@Yahoo.com$
#  Script:     DTCC_VerifyManifest_and_WorkingDir.awk
#  Revision:   $Revision: 227 $
#  Date:       $Date: 2014-11-16 06:30:37 +0000 (Sun, 16 Nov 2014) $


# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------


# Return 1 if file exists AND is readable AND isn't blank, 0 otherwise.
# Call with one param :: the filename to check.  The second param becomes a
#  local variable so that NF isn't modified.
function fileExists(fname , line) {
   if ( (getline line < fname) > 0 ) {
      close(fname) ;
      return 1 ;
   }
   else {
      return 0 ;
   }
}

# return 1 if the specified directory exists.
function dirExists(fname) {
   # ?? or ?? return !system("[ -d " quoted($1) " ] ") ;
   return !system("test -d " quoted(fname) );
}

function windows(is_windows) {
   is_windows = 0;
   if (index(tolower(ENVIRON["OS"]), "windows") > 0) {
      is_windows = 1;
   }
   return is_windows ;
}

# Return the system level / command prompt param quoted for the current OS.
# Assume Windows uses DOUBLEQUOTE and *nix uses QUOTE.
function quoted(str) {
   if ( windows() ) {
      return "\"" str "\"" ;
   }
   else {
      return "\'" str "\'" ;
   }
}

# POSIX awk does not support "length(array)"
# From: http://stackoverflow.com/a/19411381/1368561
function alen( a, i, k) {
   for( i in a) k++
   return k
}

# Return the param with leading and trailing CONTROL chars removed.
function trim( s ) {
   gsub(/^[\001-\037]+/ , "" , s) ;
   gsub(/[\001-\037]+$/ , "", s) ;
   return s ;
}


BEGIN {
   # DEBUG=1;     # or -v DEBUG=1 command line param

   if (DEBUG) print "\nDEBUG :: ------------------------------------------------------------- BEGIN\n" ;

   # Internally, both Unix and Windows paths need to use SLASH delimiter.
   # Use BACKSLASH delimiter for Windows paths passed to Windows.
   # Use SLASH delimiter for Windows paths passed to *nix (i.e. GnuWin)
   #     utils run in Windows.

   # BEGIN and main sections need to set this when exiting to cause END section to exit.
   exit_code = 0;


   # Unix paths will use SLASH.
   if ( windows() ) {
      # In Windows, filenames are not case sensitive.
      if (DEBUG)  print "DEBUG :: This is a Windows machine." ;
      IGNORECASE = 1 ;
      PATHDELIM = "\\" ;
      MANIFEST_FILELIST_DELIMITER = ";"

      WORKING_DIR    = "C:\\Users\\Public\\Documents\\Virtual Machines\\VmWareShare\\Source\\awk" ;
      MANIFEST_FNAME = "C:\\Users\\Public\\Documents\\Virtual Machines\\VmWareShare\\Source\\awk\\manifest.txt" ;
   }
   else {
      if (DEBUG)  print "DEBUG :: This is NOT a Windows machine." ;
      IGNORECASE = 0 ;
      PATHDELIM = "/" ;
      MANIFEST_FILELIST_DELIMITER = ";" ;

      WORKING_DIR = "/opt/serena/Release_Automation_Agent/core/var/work/HDF_ORACLE_HDF" ;
      MANIFEST_FNAME = "/opt/serena/Release_Automation_Agent/core/var/work/HDF_ORACLE_HDF/manifest.txt" ;
      # WORKING_DIR    = "/opt/serena/Release_Automation_Agent/core/var/work/EPN_Oracle" ;
      # MANIFEST_FNAME = "/opt/serena/Release_Automation_Agent/core/var/work/EPN_Oracle/manifest.txt" ;
   }

   if (DEBUG) printf("\nDEBUG:\n   FILENAME='%s'\n   FNR=%d\n   ARGC=%d\n   ARGV[0]='%s'\n   ARGV[1]='%s'\n\n\n",FILENAME,FNR,ARGC,ARGV[0],ARGV[1]);


   # Startup diagnostics

   # Exit with error if Working dir missing.
   if (! dirExists(WORKING_DIR)) {
      printf("Working directory '%s' not found.  Exiting with error\n",WORKING_DIR);
      exit_code = 1 ;
      exit exit_code ;
   }
   else {
      print("Working dir exists.") ;
   }


   # Exit with error if Manifest file missing.
   if (! fileExists(MANIFEST_FNAME)) {
      printf("Manifest file '%s' not found.  Exiting with error\n",MANIFEST_FNAME);
      exit_code = 2 ;
      exit exit_code ;
   }
   else {
      print("Manifest file exists.") ;
   }



   # Redirect STDIN to the manifest file.
   printf("\nPrepare to read Manifest file '%s'\n",MANIFEST_FNAME);
   ARGV[1] = MANIFEST_FNAME;
   if (ARGC < 2)
      ++ARGC ;

   if (DEBUG) printf("\nDEBUG:\n   FILENAME='%s'\n   FNR=%d\n   ARGC=%d\n   ARGV[0]='%s'\n   ARGV[1]='%s'\n\n\n",FILENAME,FNR,ARGC,ARGV[0],ARGV[1]);

   delimited_manifest_filelist = "" ;
   max_sequence_id = 0 ;

   print "\nDEBUG :: ------------------------------------------------------------- pattern-action section\n" ;

}     # End of BEGIN section

# -----------------------------------------------------------------------------

# Read the manifest file and build associative array.
# Unlike the match script, index is sequence number, value is filename.
# index value
# ----- ---------------------
#    11 file4.sql
#     2 file0021.sql
#    99 file13.txt
#     1 file123.sql
# NOTE: \b, \f, \n, \r, \t are the usual WHITESPACE suspects.

/^[\b\f\n\r\t ]*[0-9]+[\b\f\n\r\t ]+[^\b\f\n\r\t ]+/ {

   # $1 is sequence number as a string, $2 is filename.
   fname = trim($2) ;

   # Index is sequence number as an integer.  Save max.
   idx = $1 + 0;

   if (DEBUG) printf("DEBUG: Line from Manifest: '%s' ; Seq:'%s' (%d) ; fname='%s' \n",$0,$1,idx,fname) ;

   # Exit with error if the Manifest contains it's own name.
   # I sure do miss /regex/i
   if (fname ~ /^[mM][aA][nN][iI][fF][eE][sS][tT]\.[tT][xX][tT]$/) {
      print("The Manifest contains the name of the manifest file.  Exit with error.\n") ;
      exit_code = 3 ;
      exit exit_code ;
   }



   # Exit with error if dup filename entries found in the manifest.
   if ((delimited_manifest_filelist MANIFEST_FILELIST_DELIMITER) ~ (MANIFEST_FILELIST_DELIMITER  fname  MANIFEST_FILELIST_DELIMITER)) {
      printf("File '%s' repeated in Manifest at line# %d.  Exiting with error.\n",fname,NR) ;
      exit_code = 4 ;
      exit exit_code ;
   }

   # Exit with error if dup sequence numbers found in manifest.
   if ( idx in sequenced_manifest ) {
      printf("Sequence number: %d for file '%s' repeated in Manifest at line # %d.  Exiting with error.\n",idx,fname,NR) ;
      exit_code = 5 ;
      exit exit_code ;
   }

   # Exit with error if the file listed in the Manifest doesn't exist in the Working Dir.
   if (fileExists(WORKING_DIR PATHDELIM fname)) {
      printf("Found file '%s' listed in Manifest at line # %d\n",fname,NR) ;
   }
   else {
      printf("File '%s' listed in Manifest at line # %d not found in Working Dir.  Exit with error.\n",fname,NR) ;
      exit_code = 6 ;
      exit exit_code ;
   }

   # If we made it this far without problems, add current line info
   #     to the dictionary and the delimited filelist.
   sequenced_manifest[idx]=fname;
   delimited_manifest_filelist = delimited_manifest_filelist  MANIFEST_FILELIST_DELIMITER  fname ;
}

# -----------------------------------------------------------------------------

END {

   # "exit" from the BEGIN or pattern/action sections comes to END section.
   if (exit_code) exit exit_code;

   # Make the list like ";filename;filename; etc ;"
   delimited_manifest_filelist = delimited_manifest_filelist  MANIFEST_FILELIST_DELIMITER ;


   if (DEBUG) print "\nDEBUG :: ----------------------------------------------------- END SECTION\n" ;
   printf("%d sequence:filename lines in Manifest file.\n",alen(sequenced_manifest) );

   if (DEBUG)
      for (idx in sequenced_manifest) {
         printf("DEBUG: array: sequenced_manifest[%d]=%s\n",idx,sequenced_manifest[idx]);
      }

   if (DEBUG) printf("DEBUG :: Delimited Manifest Filelist='%s'\n",delimited_manifest_filelist );

   # - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - -- - --

   # Now check every file in the working directory and verify that it was/is in the Manifest.
   print("Get list of files in Working Dir");


   # ls :   -A returns all but . and ..
   #        -F append indicator (one of */=@|) to entries
   #        -p append indicator (one of  /=@|) to entries
   n_Cnt = 0 ;
   while ("ls -AF " quoted(WORKING_DIR) | getline) { workdir[n_Cnt++] = $0 }

   printf("%d inodes in Working dir.\n" , alen(workdir));

   # for (idx in workdir) {
   #    printf("DEBUG :: array: Working dir file[%d]='%s'\n",idx,workdir[idx]);
   # }

   # No files in Working Dir is an error.
   if (alen(workdir) < 1) {
      printf("Working dir is empty.  Exit with error.\n") ;
      exit 7 ;
   }

   # If the Working Dir contains one and only file, that being the Manifest, it's an error.
   if (alen(workdir) == 1  &&  workdir[0] ~ /^[mM][aA][nN][iI][fF][eE][sS][tT]\.[tT][xX][tT]$/) {
      printf("The Manifest is the only file found in the Working dir.  Exit with error.\n") ;
      exit 8 ;
   }

   # Verify Manifest against list of files in Working Dir.
   for (i=0 ; i<n_Cnt ; i++) {
      if (DEBUG)  printf("DEBUG :: Working dir file[%d]='%s'\n",i,workdir[i]);

      # Ignore anything that isn't a regular file (i.e. anything that ends
      #     with /=@|*).  Obviously if the file actually does end with one of
      #     those characters it will be mis-interpreted as a special file.

      if (match( workdir[i] , /[\/\=\@\|\*]$/ ) == 0) {

         # For everything but the Manifest file, see if the file is in the Manifest.
         if (match( workdir[i] , /^[mM][aA][nN][iI][fF][eE][sS][tT]\.[tT][xX][tT]$/ ) == 0) {
            # If Working Dir file isn't in the list of files read from the manifest then it's an error.
            if (match(delimited_manifest_filelist,MANIFEST_FILELIST_DELIMITER workdir[i] MANIFEST_FILELIST_DELIMITER ) == 0) {
               printf("File '%s' in Working Dir not found in Manifest.  Exit with error.\n",workdir[i]) ;
               exit 8 ;
            }
         }
      } else {
         # workdir[i] is a directory
         if (DEBUG) printf("DEBUG :: File '%s' is not a regular file.  Skip it.\n",workdir[i]);
      }
   }
   print("All is well.");
   exit;
}
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
